regen.mde 0.2.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +16 -0
- package/README.md +295 -0
- package/bin/build-corpus-editor.js +81 -0
- package/bin/build-corpus.js +41 -0
- package/bin/postinstall.js +187 -0
- package/bin/regen-mdeditor-install.js +27 -0
- package/bin/regen-mdeditor-uninstall.js +19 -0
- package/bin/validate-katex.js +93 -0
- package/desktop/BuildCorpusEditor/BuildCorpusBridge.cs +270 -0
- package/desktop/BuildCorpusEditor/BuildCorpusEditor.csproj +22 -0
- package/desktop/BuildCorpusEditor/EditorForm.cs +540 -0
- package/desktop/BuildCorpusEditor/Program.cs +81 -0
- package/desktop/BuildCorpusEditor/app.manifest +16 -0
- package/dist/release/regen.mde-0.2.2-win-x64-setup.exe +0 -0
- package/dist/release/regen.mde-0.2.2-win-x64.zip +0 -0
- package/dist/windows-editor/BuildCorpusEditor.deps.json +83 -0
- package/dist/windows-editor/BuildCorpusEditor.dll +0 -0
- package/dist/windows-editor/BuildCorpusEditor.exe +0 -0
- package/dist/windows-editor/BuildCorpusEditor.pdb +0 -0
- package/dist/windows-editor/BuildCorpusEditor.runtimeconfig.json +19 -0
- package/dist/windows-editor/Microsoft.Web.WebView2.Core.dll +0 -0
- package/dist/windows-editor/Microsoft.Web.WebView2.Core.xml +6817 -0
- package/dist/windows-editor/Microsoft.Web.WebView2.WinForms.dll +0 -0
- package/dist/windows-editor/Microsoft.Web.WebView2.WinForms.xml +510 -0
- package/dist/windows-editor/Microsoft.Web.WebView2.Wpf.dll +0 -0
- package/dist/windows-editor/Microsoft.Web.WebView2.Wpf.xml +1902 -0
- package/dist/windows-editor/WebView2Loader.dll +0 -0
- package/dist/windows-editor/runtimes/win-x64/native/WebView2Loader.dll +0 -0
- package/dist/windows-editor/wwwroot/assets/index-DjJ6xmhy.js +326 -0
- package/dist/windows-editor/wwwroot/assets/index-_dwMNNsm.css +1 -0
- package/dist/windows-editor/wwwroot/index.html +22 -0
- package/editor-web/index.html +21 -0
- package/editor-web/src/main.jsx +399 -0
- package/editor-web/src/styles.css +602 -0
- package/editor-web/vite.config.js +13 -0
- package/examples/build-corpus.config.example.json +21 -0
- package/installer/install-regen-mde.ps1 +175 -0
- package/installer/regen-mde.nsi +81 -0
- package/package.json +86 -0
- package/pyproject.toml +33 -0
- package/requirements.txt +4 -0
- package/scripts/build-windows-editor.ps1 +47 -0
- package/scripts/package-windows-editor.ps1 +90 -0
- package/scripts/run-corpus.ps1 +28 -0
- package/scripts/run-editor-implementation-plane.ps1 +203 -0
- package/scripts/run-required-tests.ps1 +98 -0
- package/scripts/run-smoke.ps1 +28 -0
- package/src/build_corpus/__init__.py +3 -0
- package/src/build_corpus/docx_exporter.py +798 -0
- package/src/build_corpus/exporter.py +1195 -0
- package/src/build_corpus/ppt_exporter.py +532 -0
- package/src/build_corpus/templates/__init__.py +1 -0
- package/src/build_corpus/templates/md-to-word-template.dotx +0 -0
- package/src/build_corpus/validate_assets.py +46 -0
- package/tools/audit_corpus.py +203 -0
- package/tools/collect_microsoft_word_templates.py +228 -0
- package/tools/collect_online_docx_corpus.py +272 -0
- package/tools/collect_online_pptx_corpus.py +252 -0
- package/tools/compare_pptx_inputs_outputs.py +87 -0
- package/tools/roundtrip_docx_corpus.py +171 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
Apache License
|
|
2
|
+
Version 2.0, January 2004
|
|
3
|
+
|
|
4
|
+
Copyright 2026 LIFE AI
|
|
5
|
+
|
|
6
|
+
Licensed under the Apache License, Version 2.0 (the "License");
|
|
7
|
+
you may not use this file except in compliance with the License.
|
|
8
|
+
You may obtain a copy of the License at
|
|
9
|
+
|
|
10
|
+
https://www.apache.org/licenses/LICENSE-2.0
|
|
11
|
+
|
|
12
|
+
Unless required by applicable law or agreed to in writing, software
|
|
13
|
+
distributed under the License is distributed on an "AS IS" BASIS,
|
|
14
|
+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
15
|
+
See the License for the specific language governing permissions and
|
|
16
|
+
limitations under the License.
|
package/README.md
ADDED
|
@@ -0,0 +1,295 @@
|
|
|
1
|
+
# regen.mde
|
|
2
|
+
|
|
3
|
+
regen.mde is the Windows editor and conversion suite for Markdown, Word, and PowerPoint files. Its `build-corpus` CLI converts `.docx`, `.pptx`, and `.ppt` files to Markdown while preserving the pieces that usually break in generic converters:
|
|
4
|
+
|
|
5
|
+
- Word OMML equations as KaTeX-readable TeX
|
|
6
|
+
- embedded images as local assets, base64 data URIs, or S3/R2-hosted URLs
|
|
7
|
+
- Markdown tables for simple Word tables
|
|
8
|
+
- HTML table fallback for complex tables
|
|
9
|
+
- headings, lists, links, bold, italic, inline code, and code-style paragraphs
|
|
10
|
+
- PowerPoint slide extraction with slide title detection, table mapping, and repetitive footer suppression
|
|
11
|
+
|
|
12
|
+
## Install
|
|
13
|
+
|
|
14
|
+
Python is the native runtime:
|
|
15
|
+
|
|
16
|
+
```powershell
|
|
17
|
+
pip install build-corpus
|
|
18
|
+
```
|
|
19
|
+
|
|
20
|
+
The npm package ships the Windows installer plus the conversion CLI:
|
|
21
|
+
|
|
22
|
+
```powershell
|
|
23
|
+
npm pack regen.mde
|
|
24
|
+
```
|
|
25
|
+
|
|
26
|
+
Extract the package and run `dist\release\regen.mde-<version>-win-x64-setup.exe` for a normal Windows install. The installer creates Start Menu entries for `regen.mde` and `Uninstall regen.mde`, registers right-click Explorer verbs for `.docx` and `.md`, and removes those entries during uninstall.
|
|
27
|
+
|
|
28
|
+
The legacy global npm command path is still supported for automation:
|
|
29
|
+
|
|
30
|
+
```powershell
|
|
31
|
+
npm install -g regen.mde
|
|
32
|
+
```
|
|
33
|
+
|
|
34
|
+
On Windows, the installer and supported automation paths add right-click Explorer menus for `.docx` and `.md` files under `Life AI`:
|
|
35
|
+
|
|
36
|
+
- `Life AI -> Open in regen.mde`
|
|
37
|
+
- opens `.md` directly and opens `.docx` by converting it into editable Markdown first
|
|
38
|
+
- `Life AI -> Convert to Markdown`
|
|
39
|
+
- runs `build-corpus "%1" --out-same-dir`
|
|
40
|
+
- writes `.md`, `assets`, and reports beside the source document
|
|
41
|
+
- `Life AI -> Convert to Word`
|
|
42
|
+
- runs `build-corpus "%1" --to word --out-same-dir`
|
|
43
|
+
- writes `.docx` and export report beside the source document
|
|
44
|
+
|
|
45
|
+
Set `BUILD_CORPUS_SKIP_WINDOWS_MENU=1` before a global npm install if you do not want the Explorer menu.
|
|
46
|
+
Set `BUILD_CORPUS_SKIP_EDITOR=1` before a global npm install if you want the CLI conversion verbs but not the editor open verbs.
|
|
47
|
+
|
|
48
|
+
To remove the Windows Explorer menus without uninstalling the package:
|
|
49
|
+
|
|
50
|
+
```powershell
|
|
51
|
+
build-corpus --uninstall-windows
|
|
52
|
+
```
|
|
53
|
+
|
|
54
|
+
If you uninstall the global npm package, `build-corpus` now removes those Explorer menu entries automatically during uninstall.
|
|
55
|
+
|
|
56
|
+
For a project-local install, use `npx`:
|
|
57
|
+
|
|
58
|
+
```powershell
|
|
59
|
+
npm install regen.mde
|
|
60
|
+
npx build-corpus --help
|
|
61
|
+
```
|
|
62
|
+
|
|
63
|
+
On Windows, if `build-corpus` launches a Python executable and fails with `ModuleNotFoundError`, a stale pip install is shadowing the npm command. Remove it with:
|
|
64
|
+
|
|
65
|
+
```powershell
|
|
66
|
+
py -3 -m pip uninstall build-corpus
|
|
67
|
+
```
|
|
68
|
+
|
|
69
|
+
For S3/R2 image upload support:
|
|
70
|
+
|
|
71
|
+
```powershell
|
|
72
|
+
pip install "build-corpus[s3]"
|
|
73
|
+
```
|
|
74
|
+
|
|
75
|
+
## Basic Usage
|
|
76
|
+
|
|
77
|
+
```powershell
|
|
78
|
+
build-corpus input.docx --out out
|
|
79
|
+
build-corpus deck.pptx --out out
|
|
80
|
+
build-corpus input.md --to word --out out
|
|
81
|
+
build-corpus input.md --to word --word-template C:\path\custom.dotx --out out
|
|
82
|
+
regen.mde input.md
|
|
83
|
+
regen.mdeditor input.md
|
|
84
|
+
regen-mdeditor input.md
|
|
85
|
+
build-corpus editor input.md
|
|
86
|
+
build-corpus editor input.docx
|
|
87
|
+
```
|
|
88
|
+
|
|
89
|
+
## regen.mde
|
|
90
|
+
|
|
91
|
+
regen.mde is a Windows WebView2 desktop app bundled with the package. It uses the same local Build Corpus conversion engine as the CLI:
|
|
92
|
+
|
|
93
|
+
- Markdown opens directly.
|
|
94
|
+
- Word and PowerPoint files open by converting into Markdown.
|
|
95
|
+
- Save writes Markdown.
|
|
96
|
+
- Save As writes a new Markdown file.
|
|
97
|
+
- Export DOCX writes Word output through the Markdown-to-Word route.
|
|
98
|
+
|
|
99
|
+
Build the Windows executable locally:
|
|
100
|
+
|
|
101
|
+
```powershell
|
|
102
|
+
npm run editor:windows
|
|
103
|
+
```
|
|
104
|
+
|
|
105
|
+
The executable is written to:
|
|
106
|
+
|
|
107
|
+
```text
|
|
108
|
+
dist\windows-editor\BuildCorpusEditor.exe
|
|
109
|
+
```
|
|
110
|
+
|
|
111
|
+
Convert every `.docx` in a folder:
|
|
112
|
+
|
|
113
|
+
```powershell
|
|
114
|
+
build-corpus ./word-files --out ./markdown
|
|
115
|
+
```
|
|
116
|
+
|
|
117
|
+
Convert every supported file type in a folder (`.docx`, `.pptx`, `.ppt`):
|
|
118
|
+
|
|
119
|
+
```powershell
|
|
120
|
+
build-corpus ./source-files --out ./markdown
|
|
121
|
+
```
|
|
122
|
+
|
|
123
|
+
Write Markdown beside each source document:
|
|
124
|
+
|
|
125
|
+
```powershell
|
|
126
|
+
build-corpus ./word-files --out-same-dir
|
|
127
|
+
```
|
|
128
|
+
|
|
129
|
+
## Image Modes
|
|
130
|
+
|
|
131
|
+
Local asset files, the default:
|
|
132
|
+
|
|
133
|
+
```powershell
|
|
134
|
+
build-corpus input.docx --images assets
|
|
135
|
+
```
|
|
136
|
+
|
|
137
|
+
Single-file Markdown with base64 image data URIs:
|
|
138
|
+
|
|
139
|
+
```powershell
|
|
140
|
+
build-corpus input.docx --images base64
|
|
141
|
+
```
|
|
142
|
+
|
|
143
|
+
Upload images to S3-compatible storage and write public URLs:
|
|
144
|
+
|
|
145
|
+
```powershell
|
|
146
|
+
build-corpus input.docx --images s3 --config examples\build-corpus.config.example.json
|
|
147
|
+
```
|
|
148
|
+
|
|
149
|
+
Cloudflare R2 uses the same `s3` mode. Set `endpoint_url` to:
|
|
150
|
+
|
|
151
|
+
```text
|
|
152
|
+
https://ACCOUNT_ID.r2.cloudflarestorage.com
|
|
153
|
+
```
|
|
154
|
+
|
|
155
|
+
## Config
|
|
156
|
+
|
|
157
|
+
Copy `examples/build-corpus.config.example.json` and edit it for your environment.
|
|
158
|
+
|
|
159
|
+
```json
|
|
160
|
+
{
|
|
161
|
+
"conversion": {
|
|
162
|
+
"equations": "tex",
|
|
163
|
+
"images": "s3"
|
|
164
|
+
},
|
|
165
|
+
"output": {
|
|
166
|
+
"out": "out",
|
|
167
|
+
"out_same_dir": false
|
|
168
|
+
},
|
|
169
|
+
"s3": {
|
|
170
|
+
"bucket": "build-corpus-assets",
|
|
171
|
+
"public_base_url": "https://assets.example.com",
|
|
172
|
+
"prefix": "knowledge-base",
|
|
173
|
+
"endpoint_url": "https://ACCOUNT_ID.r2.cloudflarestorage.com",
|
|
174
|
+
"region_name": "auto",
|
|
175
|
+
"access_key_id": "%R2_ACCESS_KEY_ID%",
|
|
176
|
+
"secret_access_key": "%R2_SECRET_ACCESS_KEY%"
|
|
177
|
+
}
|
|
178
|
+
}
|
|
179
|
+
```
|
|
180
|
+
|
|
181
|
+
Build Corpus expands environment variables in JSON string values, so credentials do not need to be committed.
|
|
182
|
+
|
|
183
|
+
### Output Placement
|
|
184
|
+
|
|
185
|
+
There are two output modes.
|
|
186
|
+
|
|
187
|
+
Write all converted Markdown into one output tree:
|
|
188
|
+
|
|
189
|
+
```json
|
|
190
|
+
{
|
|
191
|
+
"output": {
|
|
192
|
+
"out": "./markdown",
|
|
193
|
+
"out_same_dir": false
|
|
194
|
+
}
|
|
195
|
+
}
|
|
196
|
+
```
|
|
197
|
+
|
|
198
|
+
Write each `.md`, asset folder, and report beside the source `.docx`:
|
|
199
|
+
|
|
200
|
+
```json
|
|
201
|
+
{
|
|
202
|
+
"output": {
|
|
203
|
+
"out_same_dir": true
|
|
204
|
+
}
|
|
205
|
+
}
|
|
206
|
+
```
|
|
207
|
+
|
|
208
|
+
The same-dir mode is equivalent to:
|
|
209
|
+
|
|
210
|
+
```powershell
|
|
211
|
+
build-corpus ./word-files --out-same-dir
|
|
212
|
+
```
|
|
213
|
+
|
|
214
|
+
## Markdown to Word Templates
|
|
215
|
+
|
|
216
|
+
Markdown -> Word conversion uses this template precedence:
|
|
217
|
+
|
|
218
|
+
1. `--word-template <path>`
|
|
219
|
+
2. `word.template` in the JSON config
|
|
220
|
+
3. the bundled installed package template
|
|
221
|
+
4. built-in fallback styles if no template can be found
|
|
222
|
+
|
|
223
|
+
Template files are treated as style sources. Build Corpus creates a fresh output document body, then applies the template's Word styles, numbering, theme, fonts, and settings. It does not reuse the template body content as the exported document.
|
|
224
|
+
|
|
225
|
+
## Equations
|
|
226
|
+
|
|
227
|
+
The default equation mode is parseable TeX:
|
|
228
|
+
|
|
229
|
+
```powershell
|
|
230
|
+
build-corpus input.docx --equations tex
|
|
231
|
+
```
|
|
232
|
+
|
|
233
|
+
Equation images are only for visual debugging:
|
|
234
|
+
|
|
235
|
+
```powershell
|
|
236
|
+
build-corpus input.docx --equations image
|
|
237
|
+
```
|
|
238
|
+
|
|
239
|
+
## PowerPoint Notes
|
|
240
|
+
|
|
241
|
+
- `.pptx` is processed directly.
|
|
242
|
+
- `.ppt` is converted to `.pptx` first using LibreOffice (`soffice --headless --convert-to pptx`).
|
|
243
|
+
- Repeated boilerplate blocks that appear on most slides are removed from the emitted Markdown.
|
|
244
|
+
- Slide images are exported from the original package binaries (`ppt/media/*`), not screen-captured display rasters.
|
|
245
|
+
- Markdown output uses size-aware HTML image tags (`<img ... width= height=>`) based on OOXML display extents (`a:xfrm/a:ext`).
|
|
246
|
+
- The export report includes `low_dpi_images` to flag images whose effective on-slide DPI is under 150.
|
|
247
|
+
|
|
248
|
+
## Validation
|
|
249
|
+
|
|
250
|
+
The package includes a KaTeX validator for emitted Markdown math:
|
|
251
|
+
|
|
252
|
+
```powershell
|
|
253
|
+
build-corpus-katex out
|
|
254
|
+
```
|
|
255
|
+
|
|
256
|
+
## Repeatable Test Wrappers
|
|
257
|
+
|
|
258
|
+
Run a single known DOCX through conversion plus validators:
|
|
259
|
+
|
|
260
|
+
```powershell
|
|
261
|
+
.\scripts\run-smoke.ps1 -Docx ".\fixtures\sample.docx" -Out ".tmp\smoke" -Images assets
|
|
262
|
+
```
|
|
263
|
+
|
|
264
|
+
Run a whole folder corpus:
|
|
265
|
+
|
|
266
|
+
```powershell
|
|
267
|
+
.\scripts\run-corpus.ps1 -Source ".\fixtures\wordtest" -Out ".tmp\wordtest" -Images base64
|
|
268
|
+
```
|
|
269
|
+
|
|
270
|
+
Build a public online DOCX corpus for regression testing:
|
|
271
|
+
|
|
272
|
+
```powershell
|
|
273
|
+
python .\tools\collect_online_docx_corpus.py --out ".tmp\online-docx\source-docx" --target 50
|
|
274
|
+
.\scripts\run-corpus.ps1 -Source ".tmp\online-docx\source-docx" -Out ".tmp\online-docx\markdown"
|
|
275
|
+
```
|
|
276
|
+
|
|
277
|
+
Build a public online PPTX corpus and compare input/output extraction:
|
|
278
|
+
|
|
279
|
+
```powershell
|
|
280
|
+
python .\tools\collect_online_pptx_corpus.py --out ".tmp\online-pptx\source-pptx" --target 20
|
|
281
|
+
.\scripts\run-corpus.ps1 -Source ".tmp\online-pptx\source-pptx" -Out ".tmp\online-pptx\markdown"
|
|
282
|
+
python .\tools\compare_pptx_inputs_outputs.py --manifest ".tmp\online-pptx\source-pptx\online-pptx-manifest.json" --out ".tmp\online-pptx\markdown" --report ".tmp\online-pptx\markdown\pptx-io-compare.json"
|
|
283
|
+
```
|
|
284
|
+
|
|
285
|
+
## Failed Documents
|
|
286
|
+
|
|
287
|
+
If a document does not convert correctly, open an issue with:
|
|
288
|
+
|
|
289
|
+
- the `.docx` file if it is safe to share
|
|
290
|
+
- the generated `.md`
|
|
291
|
+
- the `export-report.json`
|
|
292
|
+
- the command and config used
|
|
293
|
+
- a screenshot of the expected Word output if layout is the issue
|
|
294
|
+
|
|
295
|
+
For confidential files, strip or replace sensitive content before sharing. The useful part is the broken DOCX structure, not the private text.
|
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
const path = require("node:path");
|
|
3
|
+
const { spawn } = require("node:child_process");
|
|
4
|
+
const fs = require("node:fs");
|
|
5
|
+
|
|
6
|
+
const root = path.resolve(__dirname, "..");
|
|
7
|
+
const callerCwd = process.cwd();
|
|
8
|
+
const candidates = [
|
|
9
|
+
path.join(root, "dist", "windows-editor", "BuildCorpusEditor.exe"),
|
|
10
|
+
path.join(root, "desktop", "BuildCorpusEditor", "bin", "Release", "net8.0-windows", "win-x64", "BuildCorpusEditor.exe"),
|
|
11
|
+
];
|
|
12
|
+
const exe = candidates.find((candidate) => fs.existsSync(candidate)) || candidates[0];
|
|
13
|
+
const args = process.argv.slice(2);
|
|
14
|
+
|
|
15
|
+
if (args.includes("--help") || args.includes("-h")) {
|
|
16
|
+
console.log(`regen.mde
|
|
17
|
+
|
|
18
|
+
Usage:
|
|
19
|
+
regen.mde [document.md|document.docx] [options]
|
|
20
|
+
regen.mdeditor [document.md|document.docx] [options]
|
|
21
|
+
regen-mdeditor [document.md|document.docx] [options]
|
|
22
|
+
build-corpus-editor [document.md|document.docx] [options]
|
|
23
|
+
|
|
24
|
+
Options:
|
|
25
|
+
--foreground, --visible Show the editor as an attached foreground process.
|
|
26
|
+
--background Launch hidden/offscreen for smoke checks.
|
|
27
|
+
--self-test Verify the editor bridge can open the document.
|
|
28
|
+
--document-self-test Open, edit, save Markdown, export Word, and reconvert.
|
|
29
|
+
--smoke-ui Run the hidden UI smoke test.
|
|
30
|
+
--out <dir> Output directory for document self-test.
|
|
31
|
+
--help, -h Show this help.
|
|
32
|
+
`);
|
|
33
|
+
process.exit(0);
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
const visible = args.includes("--foreground") || args.includes("--visible");
|
|
37
|
+
const background = args.includes("--background") || args.includes("--smoke-ui");
|
|
38
|
+
const launchArgs = normalizeLaunchArgs(args.filter((arg) => arg !== "--foreground" && arg !== "--visible"));
|
|
39
|
+
|
|
40
|
+
const child = spawn(exe, launchArgs, {
|
|
41
|
+
cwd: root,
|
|
42
|
+
stdio: "inherit",
|
|
43
|
+
windowsHide: background,
|
|
44
|
+
detached: background && !visible,
|
|
45
|
+
env: {
|
|
46
|
+
...process.env,
|
|
47
|
+
BUILD_CORPUS_ROOT: root,
|
|
48
|
+
},
|
|
49
|
+
});
|
|
50
|
+
|
|
51
|
+
child.on("error", (error) => {
|
|
52
|
+
console.error(`Build Corpus Editor executable not found: ${exe}`);
|
|
53
|
+
console.error("Build it with: npm run editor:windows");
|
|
54
|
+
console.error(error.message);
|
|
55
|
+
process.exit(1);
|
|
56
|
+
});
|
|
57
|
+
|
|
58
|
+
child.on("exit", (code) => process.exit(code ?? 0));
|
|
59
|
+
|
|
60
|
+
function normalizeLaunchArgs(rawArgs) {
|
|
61
|
+
const normalized = [];
|
|
62
|
+
for (let index = 0; index < rawArgs.length; index += 1) {
|
|
63
|
+
const arg = rawArgs[index];
|
|
64
|
+
if (arg === "--out" && index < rawArgs.length - 1) {
|
|
65
|
+
normalized.push(arg, resolveFromCaller(rawArgs[index + 1]));
|
|
66
|
+
index += 1;
|
|
67
|
+
continue;
|
|
68
|
+
}
|
|
69
|
+
if (index === 0 && !arg.startsWith("--")) {
|
|
70
|
+
normalized.push(resolveFromCaller(arg));
|
|
71
|
+
continue;
|
|
72
|
+
}
|
|
73
|
+
normalized.push(arg);
|
|
74
|
+
}
|
|
75
|
+
return normalized;
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
function resolveFromCaller(value) {
|
|
79
|
+
if (!value || path.isAbsolute(value)) return value;
|
|
80
|
+
return path.resolve(callerCwd, value);
|
|
81
|
+
}
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
const { spawnSync } = require("node:child_process");
|
|
3
|
+
const path = require("node:path");
|
|
4
|
+
|
|
5
|
+
const root = path.resolve(__dirname, "..");
|
|
6
|
+
const candidates = process.platform === "win32" ? ["py", "python"] : ["python3", "python"];
|
|
7
|
+
const env = { ...process.env };
|
|
8
|
+
env.PYTHONPATH = env.PYTHONPATH ? `${path.join(root, "src")}${path.delimiter}${env.PYTHONPATH}` : path.join(root, "src");
|
|
9
|
+
const args = process.argv.slice(2);
|
|
10
|
+
|
|
11
|
+
if (args[0] === "editor") {
|
|
12
|
+
const editor = path.join(root, "bin", "build-corpus-editor.js");
|
|
13
|
+
const result = spawnSync(process.execPath, [editor, ...args.slice(1)], {
|
|
14
|
+
stdio: "inherit",
|
|
15
|
+
cwd: root,
|
|
16
|
+
env: { ...env, BUILD_CORPUS_ROOT: root },
|
|
17
|
+
});
|
|
18
|
+
process.exit(result.status ?? 1);
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
if (process.platform === "win32" && args.includes("--uninstall-windows")) {
|
|
22
|
+
const result = spawnSync(process.execPath, [path.join(root, "bin", "postinstall.js"), "--uninstall"], {
|
|
23
|
+
stdio: "inherit",
|
|
24
|
+
cwd: root,
|
|
25
|
+
env,
|
|
26
|
+
});
|
|
27
|
+
process.exit(result.status ?? 1);
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
let result = null;
|
|
31
|
+
for (const candidate of candidates) {
|
|
32
|
+
const args = candidate === "py"
|
|
33
|
+
? ["-3", "-m", "build_corpus.exporter", ...process.argv.slice(2)]
|
|
34
|
+
: ["-m", "build_corpus.exporter", ...process.argv.slice(2)];
|
|
35
|
+
result = spawnSync(candidate, args, { stdio: "inherit", cwd: root, env });
|
|
36
|
+
if (result.error && result.error.code === "ENOENT") continue;
|
|
37
|
+
process.exit(result.status ?? 1);
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
console.error("Build Corpus requires Python 3.10+ on PATH.");
|
|
41
|
+
process.exit(1);
|
|
@@ -0,0 +1,187 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
const { spawnSync } = require("node:child_process");
|
|
3
|
+
const path = require("node:path");
|
|
4
|
+
|
|
5
|
+
const UNINSTALL_MODE = process.argv.includes("--uninstall");
|
|
6
|
+
const SKIP_PIP_INSTALL = process.env.BUILD_CORPUS_SKIP_PIP_INSTALL === "1";
|
|
7
|
+
|
|
8
|
+
const packageRoot = path.resolve(__dirname, "..");
|
|
9
|
+
const candidates = process.platform === "win32" ? ["py", "python"] : ["python3", "python"];
|
|
10
|
+
const requirements = path.join(packageRoot, "requirements.txt");
|
|
11
|
+
|
|
12
|
+
function isGlobalInstall() {
|
|
13
|
+
return process.env.npm_config_global === "true";
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
function printUsage() {
|
|
17
|
+
const isGlobal = isGlobalInstall();
|
|
18
|
+
if (isGlobal) {
|
|
19
|
+
console.log("Build Corpus installed globally. Run: build-corpus --help");
|
|
20
|
+
return;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
console.log("Build Corpus installed locally.");
|
|
24
|
+
console.log("Run with: npx build-corpus --help");
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
function warnOnShadowedWindowsCommand() {
|
|
28
|
+
if (process.platform !== "win32") return;
|
|
29
|
+
|
|
30
|
+
const whereResult = spawnSync("where.exe", ["build-corpus"], { encoding: "utf8" });
|
|
31
|
+
if (whereResult.error || whereResult.status !== 0) return;
|
|
32
|
+
|
|
33
|
+
const matches = whereResult.stdout
|
|
34
|
+
.split(/\r?\n/)
|
|
35
|
+
.map((line) => line.trim())
|
|
36
|
+
.filter(Boolean);
|
|
37
|
+
if (matches.length === 0) return;
|
|
38
|
+
|
|
39
|
+
const firstMatch = matches[0].toLowerCase();
|
|
40
|
+
if (!firstMatch.includes("python") || !firstMatch.endsWith("build-corpus.exe")) return;
|
|
41
|
+
|
|
42
|
+
console.warn("");
|
|
43
|
+
console.warn("WARNING: 'build-corpus' currently resolves to a Python executable:");
|
|
44
|
+
console.warn(` ${matches[0]}`);
|
|
45
|
+
console.warn("That usually means a stale pip install is shadowing the npm command.");
|
|
46
|
+
console.warn("Fix it with: py -3 -m pip uninstall build-corpus");
|
|
47
|
+
console.warn("Then rerun your npm install or use: npx build-corpus --help");
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
function installWindowsContextMenu() {
|
|
51
|
+
if (process.platform !== "win32") return;
|
|
52
|
+
if (!isGlobalInstall()) return;
|
|
53
|
+
if (process.env.BUILD_CORPUS_SKIP_WINDOWS_MENU === "1") return;
|
|
54
|
+
|
|
55
|
+
const prefix = process.env.npm_config_prefix || path.join(process.env.APPDATA || "", "npm");
|
|
56
|
+
const commandPath = path.join(prefix, "build-corpus.cmd");
|
|
57
|
+
const editorPath = path.join(prefix, "build-corpus-editor.cmd");
|
|
58
|
+
const nativeEditorPath = path.join(packageRoot, "dist", "windows-editor", "BuildCorpusEditor.exe");
|
|
59
|
+
const editorCommandPath = fileExists(nativeEditorPath) ? nativeEditorPath : editorPath;
|
|
60
|
+
const menus = [
|
|
61
|
+
{
|
|
62
|
+
keyName: "BuildCorpusToMarkdown",
|
|
63
|
+
label: "Convert to Markdown",
|
|
64
|
+
commandValue: `"${commandPath}" "%1" --out-same-dir`,
|
|
65
|
+
extension: ".docx",
|
|
66
|
+
},
|
|
67
|
+
...(process.env.BUILD_CORPUS_SKIP_EDITOR === "1" ? [] : [
|
|
68
|
+
{
|
|
69
|
+
keyName: "BuildCorpusOpenEditor",
|
|
70
|
+
label: "Open in regen.mde",
|
|
71
|
+
commandValue: `"${editorCommandPath}" "%1"`,
|
|
72
|
+
extension: ".docx",
|
|
73
|
+
},
|
|
74
|
+
{
|
|
75
|
+
keyName: "BuildCorpusOpenEditor",
|
|
76
|
+
label: "Open in regen.mde",
|
|
77
|
+
commandValue: `"${editorCommandPath}" "%1"`,
|
|
78
|
+
extension: ".md",
|
|
79
|
+
},
|
|
80
|
+
]),
|
|
81
|
+
{
|
|
82
|
+
keyName: "BuildCorpusToWord",
|
|
83
|
+
label: "Convert to Word",
|
|
84
|
+
commandValue: `"${commandPath}" "%1" --to word --out-same-dir`,
|
|
85
|
+
extension: ".md",
|
|
86
|
+
},
|
|
87
|
+
];
|
|
88
|
+
|
|
89
|
+
for (const menu of menus) {
|
|
90
|
+
const commands = [
|
|
91
|
+
["delete", `HKCU\\Software\\Classes\\SystemFileAssociations\\${menu.extension}\\shell\\Life AI`, "/f"],
|
|
92
|
+
["delete", `HKCU\\Software\\Microsoft\\Windows\\CurrentVersion\\Explorer\\CommandStore\\shell\\LifeAI.${menu.keyName}`, "/f"],
|
|
93
|
+
["delete", `HKCU\\Software\\LifeAI\\ShellCommands\\${menu.keyName}`, "/f"],
|
|
94
|
+
["add", `HKCU\\Software\\Classes\\SystemFileAssociations\\${menu.extension}\\shell\\${menu.keyName}`, "/v", "MUIVerb", "/d", menu.label, "/f"],
|
|
95
|
+
["add", `HKCU\\Software\\Classes\\SystemFileAssociations\\${menu.extension}\\shell\\${menu.keyName}`, "/v", "Icon", "/t", "REG_EXPAND_SZ", "/d", menu.keyName === "BuildCorpusOpenEditor" ? editorCommandPath : commandPath, "/f"],
|
|
96
|
+
["add", `HKCU\\Software\\Classes\\SystemFileAssociations\\${menu.extension}\\shell\\${menu.keyName}\\command`, "/ve", "/d", menu.commandValue, "/f"],
|
|
97
|
+
];
|
|
98
|
+
|
|
99
|
+
for (const args of commands) {
|
|
100
|
+
const result = spawnSync("reg.exe", args, { stdio: "inherit" });
|
|
101
|
+
const status = result.status ?? 1;
|
|
102
|
+
if (args[0] === "delete" && status === 1 && !result.error) {
|
|
103
|
+
continue;
|
|
104
|
+
}
|
|
105
|
+
if (result.error || status !== 0) {
|
|
106
|
+
console.warn(`Build Corpus could not add the Windows ${menu.extension} context menu.`);
|
|
107
|
+
return;
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
console.log("Windows Explorer menus added for .docx and .md");
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
function fileExists(candidate) {
|
|
116
|
+
try {
|
|
117
|
+
return require("node:fs").existsSync(candidate);
|
|
118
|
+
} catch {
|
|
119
|
+
return false;
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
function uninstallWindowsContextMenu() {
|
|
124
|
+
if (process.platform !== "win32") return 0;
|
|
125
|
+
|
|
126
|
+
const menus = [
|
|
127
|
+
{ extension: ".docx", keyName: "BuildCorpusToMarkdown" },
|
|
128
|
+
{ extension: ".docx", keyName: "BuildCorpusOpenEditor" },
|
|
129
|
+
{ extension: ".md", keyName: "BuildCorpusOpenEditor" },
|
|
130
|
+
{ extension: ".md", keyName: "BuildCorpusToWord" },
|
|
131
|
+
];
|
|
132
|
+
|
|
133
|
+
let exitCode = 0;
|
|
134
|
+
for (const menu of menus) {
|
|
135
|
+
const removals = [
|
|
136
|
+
`HKCU\\Software\\Classes\\SystemFileAssociations\\${menu.extension}\\shell\\BuildCorpus`,
|
|
137
|
+
`HKCU\\Software\\Classes\\SystemFileAssociations\\${menu.extension}\\shell\\${menu.keyName}`,
|
|
138
|
+
`HKCU\\Software\\Classes\\SystemFileAssociations\\${menu.extension}\\shell\\Life AI`,
|
|
139
|
+
`HKCU\\Software\\Microsoft\\Windows\\CurrentVersion\\Explorer\\CommandStore\\shell\\LifeAI.${menu.keyName}`,
|
|
140
|
+
`HKCU\\Software\\LifeAI\\ShellCommands\\${menu.keyName}`,
|
|
141
|
+
];
|
|
142
|
+
|
|
143
|
+
for (const key of removals) {
|
|
144
|
+
const result = spawnSync("reg.exe", ["delete", key, "/f"], { stdio: "inherit" });
|
|
145
|
+
const status = result.status ?? 1;
|
|
146
|
+
if (result.error || (status !== 0 && status !== 1)) {
|
|
147
|
+
console.warn(`Build Corpus could not remove the Windows ${menu.extension} context menu.`);
|
|
148
|
+
exitCode = 1;
|
|
149
|
+
}
|
|
150
|
+
}
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
console.log("Windows Explorer menus removed for .docx and .md");
|
|
154
|
+
return exitCode;
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
if (UNINSTALL_MODE) {
|
|
158
|
+
process.exit(uninstallWindowsContextMenu());
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
if (SKIP_PIP_INSTALL) {
|
|
162
|
+
printUsage();
|
|
163
|
+
installWindowsContextMenu();
|
|
164
|
+
warnOnShadowedWindowsCommand();
|
|
165
|
+
process.exit(0);
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
for (const candidate of candidates) {
|
|
169
|
+
const args = candidate === "py"
|
|
170
|
+
? ["-3", "-m", "pip", "install", "-r", requirements]
|
|
171
|
+
: ["-m", "pip", "install", "-r", requirements];
|
|
172
|
+
const result = spawnSync(candidate, args, { stdio: "inherit" });
|
|
173
|
+
if (result.error && result.error.code === "ENOENT") continue;
|
|
174
|
+
if ((result.status ?? 1) !== 0) {
|
|
175
|
+
process.exit(result.status ?? 1);
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
printUsage();
|
|
179
|
+
installWindowsContextMenu();
|
|
180
|
+
warnOnShadowedWindowsCommand();
|
|
181
|
+
process.exit(0);
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
console.warn("Build Corpus could not find Python to install Python dependencies.");
|
|
185
|
+
printUsage();
|
|
186
|
+
installWindowsContextMenu();
|
|
187
|
+
process.exit(0);
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
const { spawnSync } = require("node:child_process");
|
|
3
|
+
const path = require("node:path");
|
|
4
|
+
|
|
5
|
+
if (process.platform !== "win32") {
|
|
6
|
+
console.error("regen.mde installer is only available on Windows.");
|
|
7
|
+
process.exit(1);
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
const root = path.resolve(__dirname, "..");
|
|
11
|
+
const script = path.join(root, "installer", "install-regen-mde.ps1");
|
|
12
|
+
const result = spawnSync("powershell.exe", [
|
|
13
|
+
"-NoProfile",
|
|
14
|
+
"-ExecutionPolicy",
|
|
15
|
+
"Bypass",
|
|
16
|
+
"-File",
|
|
17
|
+
script,
|
|
18
|
+
"-PackageRoot",
|
|
19
|
+
root,
|
|
20
|
+
...process.argv.slice(2),
|
|
21
|
+
], {
|
|
22
|
+
stdio: "inherit",
|
|
23
|
+
cwd: root,
|
|
24
|
+
windowsHide: true,
|
|
25
|
+
});
|
|
26
|
+
|
|
27
|
+
process.exit(result.status ?? 1);
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
const { spawnSync } = require("node:child_process");
|
|
3
|
+
const path = require("node:path");
|
|
4
|
+
|
|
5
|
+
if (process.platform !== "win32") {
|
|
6
|
+
console.error("regen.mde uninstaller is only available on Windows.");
|
|
7
|
+
process.exit(1);
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
const root = path.resolve(__dirname, "..");
|
|
11
|
+
const result = spawnSync(process.execPath, [
|
|
12
|
+
path.join(root, "bin", "postinstall.js"),
|
|
13
|
+
"--uninstall",
|
|
14
|
+
], {
|
|
15
|
+
stdio: "inherit",
|
|
16
|
+
cwd: root,
|
|
17
|
+
});
|
|
18
|
+
|
|
19
|
+
process.exit(result.status ?? 1);
|